#define _GNU_SOURCE
#include <sys/file.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>
#include "log.h"
//#include "list.h"
#include "queue.h"
#define	LOG_BUF_SIZE	8192

struct mr_log_s {
	LogLevel level;
	FILE* fp;
	char*  m_filelocation;
	int console;
	//mr_list_p log_list;
	mr_queue_p log_list;
	pthread_t thread_main;
	int logclose;
	u_char need_flush ;
	size_t file_size;
	size_t cur_file_size;
};

int mr_logger_close(mr_log_p plog);
void *mr_logger_thread(void * ptr);
int mr_file_open(char * file_location,FILE** fp);

int mr_file_open(char * file_location,FILE** fp)
{
	FILE * mfp;
	mfp  = fopen(file_location,"w");
	setvbuf(mfp, NULL, _IOFBF, LOG_BUF_SIZE);
	if(mfp == NULL)
	{
		printf("Error in openeing file %s\n",file_location);
		//exit(0);
		return -1;
	}
	*fp=mfp;
	return 0;
}

int  mr_logger_init(mr_log_p *plog,LogLevel l,char *filelocation ,size_t file_size,int console)
{
	int thread_id;
	mr_log_p mylog;
	mylog  =(mr_log_p)malloc(sizeof(mr_log_t));
	memset(mylog, 0, sizeof(mr_log_t));
	//mr_new_list(&(mylog->log_list));
	mr_queue_init(&(mylog->log_list), free);
	mylog->level = l;
	mylog->m_filelocation = filelocation;
	mylog->file_size =file_size;
	mr_file_open(mylog->m_filelocation,&(mylog->fp));
	mylog->console=console;
	mylog->logclose=0;
	thread_id=pthread_create(&(mylog->thread_main),NULL,(void *)&mr_logger_thread,(void *)(mylog) );
	if(thread_id < 0 )  printf("error creating thread\n");	
	*plog = mylog;
	return 0;
}


void *mr_logger_thread(void * ptr)
{
	char *pdata;
	mr_log_p plog=(mr_log_p)ptr;
	struct timespec ts = { 1, 0 };
	int datasize;
	while(!plog->logclose)
	{
//		printf("%p waiting\n", plog);
		if( ( mr_queue_pop(plog->log_list,(void **)&pdata,&datasize, &ts)) ) {
//			printf("%p done.\n", plog);
			if( plog->need_flush ) {
				printf("Flush %p.\n", plog);
				fflush(plog->fp);
				plog->need_flush = 0;
			}
			continue;
		}
		if(pdata)
		{
		plog->cur_file_size += fwrite(pdata, 1, strlen(pdata), plog->fp);
			if(plog->console)
			{
			fprintf(stdout,"%s",pdata);
			}
			
		free(pdata);		 
		}
		if( plog->file_size && plog->cur_file_size>=plog->file_size)
		{
			int len ;
			char * buffer;
			time_t t;
			struct tm tm;
			fflush(plog->fp);
			fclose(plog->fp);
			time(&t);
		       	localtime_r(&t, &tm);
			len =strlen(plog->m_filelocation);
			buffer = malloc((len+25) *sizeof(char));
			len = sprintf(buffer, "%s_%.2d_%.2d_%.4d_%.2d_%.2d_%.2d.log",
					plog->m_filelocation,
					tm.tm_mday, tm.tm_mon+1, tm.tm_year+1900,
					tm.tm_hour, tm.tm_min, tm.tm_sec);
	//		printf("filename %s\n",buffer);
			rename(plog->m_filelocation,buffer);
			mr_file_open(plog->m_filelocation,&(plog->fp));
			free(buffer);
			plog->cur_file_size= 0;
		}
		plog->need_flush = 1;
		//free(pdata);
	}
	printf("Log %p existing.\n", plog);
	return NULL;
}

int mr_logger_write(mr_log_p plog,LogLevel l, const char* logformat,...)
{
	va_list aptr;
	char * buffer;
	int len ;
        time_t t;
	struct tm tm;

	if(l>plog->level)
	{
		return 1;
	}
	time(&t);
	localtime_r(&t, &tm);
	va_start(aptr,logformat);
	len = vsnprintf(NULL, 0, logformat, aptr ); 
	va_end(aptr);
	buffer =(char *) malloc( (len+21) * sizeof(char) );
	//memset(buffer,0,(len+21)*sizeof(char));
	va_start(aptr,logformat);
	vsnprintf(buffer + 20,len+1,logformat, aptr);
	va_end(aptr);
	len = sprintf(buffer, "%.2d/%.2d/%.4d %.2d:%.2d:%.2d",
			tm.tm_mday, tm.tm_mon+1, tm.tm_year+1900,
			tm.tm_hour, tm.tm_min, tm.tm_sec);
	buffer[len] = ' ';
	if(buffer){
		
		//mr_list_push(plog->log_list,(void *)buffer,strlen(buffer)+1);
		mr_queue_push(plog->log_list,(void *)buffer,strlen(buffer)+1);
//free(buffer);
	}
	return 0;
}

int mr_logger_close(mr_log_p plog)
{		
	pthread_join(plog->thread_main,NULL);
	if(plog->fp == NULL)
		return 0;
	fflush(plog->fp);
	fclose(plog->fp);
	plog->fp = NULL;
	return 0;
}
int mr_logger_destroy(mr_log_p plog)
{
	plog->logclose=1;
	mr_queue_close(plog->log_list);
	mr_logger_close(plog);
	mr_queue_destroy(plog->log_list);
	free(plog);
	return 0;
}


LogLevel mr_logger_get_level(mr_log_p plog) {
	return plog->level;
}
